/* -----------------------------------------------------------------------------
 * Copyright (c) 2014 ARM Ltd.
 *
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the authors be held liable for any damages arising from
 * the use of this software. Permission is granted to anyone to use this
 * software for any purpose, including commercial applications, and to alter
 * it and redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software in
 *    a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source distribution.
 *
 *
 * $Date:        15. April 2014
 * $Revision:    V1.00
 *
 * Project:      Flash Programming Functions for STMicroelectronics STM32F4xx Flash
 * --------------------------------------------------------------------------- */

/* History:
 *  Version 1.00
 *    Initial release
 */

/*
   Mandatory Flash Programming Functions (Called by FlashOS):
                int Init        (unsigned long adr,   // Initialize Flash
                                 unsigned long clk,
                                 unsigned long fnc);
                int UnInit      (unsigned long fnc);  // De-initialize Flash
                int EraseSector (unsigned long adr);  // Erase Sector Function
                int ProgramPage (unsigned long adr,   // Program Page Function
                                 unsigned long sz,
                                 unsigned char *buf);

   Optional  Flash Programming Functions (Called by FlashOS):
                int BlankCheck  (unsigned long adr,   // Blank Check
                                 unsigned long sz,
                                 unsigned char pat);
                int EraseChip   (void);               // Erase complete Device
      unsigned long Verify      (unsigned long adr,   // Verify Function
                                 unsigned long sz,
                                 unsigned char *buf);

       - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
       - Verify       is necessary if Flash space is not mapped into CPU memory space
       - if EraseChip is not provided than EraseSector for all sectors is called
*/

#include "FlashOS.H" // FlashOS Structures

#include <stdint.h>
#include <string.h>

#include "main.h"
#include "spi.h"
#include "gpio.h"
#include "norflash.h"

extern void SystemClock_Config(void);

HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  return HAL_OK;
}

void HAL_Delay(uint32_t Delay)
{
  volatile int i = 0;
  for (i = 0; i < 1000; i++)
    ;
}

uint32_t HAL_GetTick(void)
{
  volatile int i;
  for (i = 0; i < 1000; i++)
    ;
  uwTick++;
  return uwTick;
}

void LED_R_ENABLE(void)
{
  HAL_GPIO_WritePin(R_GPIO_Port, R_Pin, GPIO_PIN_RESET);
}

void LED_R_DISBLE(void)
{
  HAL_GPIO_WritePin(R_GPIO_Port, R_Pin, GPIO_PIN_SET);
}

void LED_G_ENABLE(void)
{
  HAL_GPIO_WritePin(G_GPIO_Port, G_Pin, GPIO_PIN_RESET);
}

void LED_G_DISBLE(void)
{
  HAL_GPIO_WritePin(G_GPIO_Port, G_Pin, GPIO_PIN_SET);
}

void LED_B_ENABLE(void)
{
  HAL_GPIO_WritePin(B_GPIO_Port, B_Pin, GPIO_PIN_RESET);
}

void LED_B_DISBLE(void)
{
  HAL_GPIO_WritePin(B_GPIO_Port, B_Pin, GPIO_PIN_SET);
}

void LED_R_Toggle(void)
{
  HAL_GPIO_TogglePin(R_GPIO_Port, R_Pin);
}

void LED_G_Toggle(void)
{
  HAL_GPIO_TogglePin(G_GPIO_Port, G_Pin);
}

void LED_B_Toggle(void)
{
  HAL_GPIO_TogglePin(B_GPIO_Port, B_Pin);
}

/*
 *  Initialize Flash Programming Functions
 *    Parameter:      adr:  Device Base Address
 *                    clk:  Clock Frequency (Hz)
 *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */
uint32_t base_adr = 0;
uint8_t flash_id[3];
int Init(unsigned long adr, unsigned long clk, unsigned long fnc)
{
  base_adr = adr;

  SystemInit();
  HAL_Init();
  SystemClock_Config();
  hspi2.Instance = SPI2;
  HAL_SPI_DeInit(&hspi2);
  MX_GPIO_Init();
  // MX_USART1_UART_Init();
  // prints("Init \r\n");
  MX_SPI2_Init();
  // LED_G_ENABLE();
  // NORFLASH_DeviceID(flash_id);
  NORFLASH_Reset();
  for (int i = 0; i < 500000; i++)
    ;
  NORFLASH_DeviceID(flash_id);
  if (flash_id[0] == 0xc2 && flash_id[0] == 0x20 && flash_id[0] == 0x16)
  {
    LED_G_ENABLE();
  }
  return (0);
}

/*
 *  De-Initialize Flash Programming Functions
 *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int UnInit(unsigned long fnc)
{
  HAL_SPI_DeInit(&hspi2);
  LED_R_DISBLE();
  LED_G_DISBLE();
  return (0);
}

/*
 *  Erase complete Flash Memory
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseChip(void)
{
#if 1
  //MX_GPIO_Init();
  //HAL_SPI_DeInit(&hspi2);
  //MX_SPI2_Init();
  //NORFLASH_Reset();
  //NORFLASH_DeviceID(flash_id);
  if (NORFLASH_EraseChip() != NORFLASH_OK) return (1);
  LED_R_Toggle();
  return (0);
#else
  return (1);
#endif
}

/*- BlankCheck (...) -----------------------------------------------------------
 *
 *  Blank Check Checks if Memory is Blank
 *    Parameter:      adr:  Block Start Address
 *                    sz:   Block Size (in bytes)
 *                    pat:  Block Pattern
 *    Return Value:   0 - OK,  1 - Failed
 *
 *  Parameters
 *    adr Block start address
 *    sz  Block size in bytes
 *    pat Pattern to compare
 *  Returns
 *    status information:
 *      0 when the block content is equal to the pattern pat.
 *      1 when the block content differs from the pattern pat.
 *  The function BlankCheck can be used to check whether the specified block is empty,
 *  or whether the content is equal to a specific pattern defined in the argument pat.
 *  The argument adr specifies the start address of the block that is to be verified.
 *  The argument sz specifies the size of the block that is to be verified.
 */
// 对于非映射地址，这个函数是必须的。
// 直接返回1，强制开启扇区擦写。
int BlankCheck(unsigned long adr, unsigned long sz, unsigned char pat)
{
  return (1); // Always Force Erase
}

/*
 *  Erase Sector in Flash Memory
 *    Parameter:      adr:  Sector Address
 *    Return Value:   0 - OK,  1 - Failed
 */
#if 1
int EraseSector(unsigned long adr)
{
  //MX_GPIO_Init();
  //HAL_SPI_DeInit(&hspi2);
  //MX_SPI2_Init();
  //NORFLASH_Reset();
  //NORFLASH_DeviceID(flash_id);
  if (NORFLASH_EraseBlock(adr - base_adr) != NORFLASH_OK) return (1);
  LED_R_Toggle();
  return (0);
}
#endif
/*
 *  Program Page in Flash Memory
 *    Parameter:      adr:  Page Start Address
 *                    sz:   Page Size
 *                    buf:  Page Data
 *    Return Value:   0 - OK,  1 - Failed
 */

int ProgramPage(unsigned long adr, unsigned long sz, unsigned char *buf)
{
  //MX_GPIO_Init();
  //HAL_SPI_DeInit(&hspi2);
  //MX_SPI2_Init();
  //NORFLASH_Reset();
  //NORFLASH_DeviceID(flash_id);
  if (NORFLASH_Write(buf, adr - base_adr, sz) != NORFLASH_OK) return (1);
  LED_R_Toggle();
  return (0);
}

#if 0
unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf)
{
  uint8_t *p = (uint8_t *)adr;

  for (int i = 0; i < sz; i++)
  {
    if (*p != buf[i])
    {
      return ((unsigned long)p);
    }
    p++;
  }

  return (adr + sz);
}
#else
static uint8_t flash_buf[4096]; // Programming Page Size
unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf)
{
  uint32_t addr = adr - base_adr;
#if 0
  uint32_t err_addr;
  uint32_t VerifiedData = 0;
  uint32_t CurrentData = 0;

  //MX_GPIO_Init();
  //HAL_SPI_DeInit(&hspi2);
  //MX_SPI2_Init();
  //NORFLASH_Reset();
  //NORFLASH_DeviceID(flash_id);

  while (sz > VerifiedData)
  {
    CurrentData = ((sz - VerifiedData) >= NORFLASH_PAGE_SIZE) ? NORFLASH_PAGE_SIZE : (sz - VerifiedData);
    NORFLASH_Read(flash_buf, addr, CurrentData);

    for (volatile int i = 0; i < CurrentData; i++)
    {
      if (flash_buf[i] != buf[VerifiedData + i])
      {
        err_addr = addr + base_adr + i;
        err_addr = err_addr - (err_addr % sizeof(unsigned long));
        return (err_addr);
      }
    }

    VerifiedData += CurrentData;
    addr += CurrentData;
  }
#else
  if (sz > sizeof(flash_buf)) return (addr + base_adr);
  NORFLASH_Read(flash_buf, addr, sz);
  for (volatile int i = 0; i < sz; i++)
  {
    if (flash_buf[i] != buf[i])
      {
        return (addr + base_adr + i);
      }
  }
#endif
  LED_R_Toggle();
  return (addr + base_adr + sz);
}
#endif
